<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="LslPl" id="LslPl"><span data-lake-id="u18036ccf" id="u18036ccf">典型回答</span></h1>
  <p data-lake-id="u5b958fb5" id="u5b958fb5"><br></p>
  <p data-lake-id="u5d8bdfa4" id="u5d8bdfa4"><span data-lake-id="uca9f65de" id="uca9f65de">在Spring框架中，使用@PostConstruct、自定义的init-method方法，和InitializingBean接口和afterPropertiesSet方法都是用于在Bean初始化阶段执行特定方法的方式。他们的执行顺序是：</span><strong><span data-lake-id="u11ea712e" id="u11ea712e">构造函数&gt;@PostConstruct &gt; afterPropertiesSet &gt; init-method</span></strong></p>
  <p data-lake-id="ub914f6e8" id="ub914f6e8"><span data-lake-id="u77fd26a4" id="u77fd26a4">​</span><br></p>
  <ul list="ubc3cfc2d">
   <li fid="u6c5231e7" data-lake-id="u0f02d40c" id="u0f02d40c"><strong><span data-lake-id="ue12b2967" id="ue12b2967">@PostConstruct</span></strong><span data-lake-id="u30db9237" id="u30db9237"> 是javax.annotation 包中的注解(Spring Boot 3.0之后jakarta.annotation中，用于在构造函数执行完毕并且依赖注入完成后执行特定的初始化方法。标注在方法上，表示这个方法将在Bean初始化阶段被调用。</span></li>
   <li fid="u6c5231e7" data-lake-id="uff97cfb4" id="uff97cfb4"><strong><span data-lake-id="u41ad1c17" id="u41ad1c17">init-method</span></strong><span data-lake-id="u009fe5b4" id="u009fe5b4"> 是在Spring配置文件（如XML文件）中配置的一种方式。通过在Bean的配置中指定 init-method 属性，可以告诉Spring在Bean初始化完成后调用指定的初始化方法。如果不使用xml文件，也可以使用 @Bean 注解的 initMethod 属性来指定初始化方法。（下面的例子就是用的这种方式）</span></li>
   <li fid="u6c5231e7" data-lake-id="u0d87905e" id="u0d87905e"><strong><span data-lake-id="u43f12a19" id="u43f12a19">afterPropertiesSet</span></strong><span data-lake-id="u1b9e9589" id="u1b9e9589"> 是 Spring 的 InitializingBean 接口中的方法。如果一个 Bean 实现了 InitializingBean 接口，Spring 在初始化阶段会调用该接口的 afterPropertiesSet 方法。</span></li>
  </ul>
  <p data-lake-id="u2cda8663" id="u2cda8663"><br></p>
  <p data-lake-id="ud692c5c4" id="ud692c5c4"><span data-lake-id="ubfabfa1c" id="ubfabfa1c">​</span><br></p>
  <p data-lake-id="u90ccb2d2" id="u90ccb2d2"><span data-lake-id="u28a67026" id="u28a67026">Talk is Cheap，Show me the Code ，如下是一个示例：</span></p>
  <p data-lake-id="ub9055017" id="ub9055017"><span data-lake-id="uac979c22" id="uac979c22">​</span><br></p>
  <pre lang="java"><code>
import jakarta.annotation.PostConstruct;
//因为我用的是Spring Boot 3.0.如果是2.0需要改成javax.annotation.PostConstruct
import org.springframework.beans.factory.InitializingBean;

public class ExampleBean implements InitializingBean {

    private String message;

    public ExampleBean() {
        System.out.println("构造函数执行");
    }
    
    @PostConstruct
    public void postConstructMethod() {
        System.out.println("@PostConstruct执行");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet执行");
    }

    public void customInitMethod() {
        System.out.println("init-method执行");
    }
}

</code></pre>
  <p data-lake-id="u99d8c786" id="u99d8c786"><br></p>
  <pre lang="java"><code>
package cn.hollis;

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class ExampleConfig {

    @Bean(initMethod = "customInitMethod")
    public ExampleBean exampleBean(){
        return new ExampleBean();
    }
}

</code></pre>
  <p data-lake-id="uda0fa368" id="uda0fa368"><br></p>
  <p data-lake-id="u41fc0aa3" id="u41fc0aa3"><span data-lake-id="u3c9a2632" id="u3c9a2632">启动Spring：</span></p>
  <pre lang="java"><code>
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(scanBasePackages = "cn.hollis")
public class TestMain {

    public static void main(String[] args) {
        SpringApplication.run(TestMain.class, args);
    }

}

</code></pre>
  <p data-lake-id="u305fb880" id="u305fb880"><br></p>
  <p data-lake-id="u40b70bba" id="u40b70bba"><span data-lake-id="u32dbf9f3" id="u32dbf9f3">输出结果：·</span></p>
  <p data-lake-id="uedd12271" id="uedd12271"><span data-lake-id="u698c50c7" id="u698c50c7">​</span><br></p>
  <pre lang="java"><code>
构造函数执行
@PostConstruct执行
afterPropertiesSet执行
init-method执行
</code></pre>
  <p data-lake-id="u62a84b8a" id="u62a84b8a"><br></p>
  <p data-lake-id="ub4e5d3ba" id="ub4e5d3ba"><span data-lake-id="u19016f03" id="u19016f03">所以，他们的执行顺序就是</span><strong><span data-lake-id="ufe73e88e" id="ufe73e88e">构造函数&gt;@PostConstruct &gt; afterPropertiesSet &gt; init-method</span></strong></p>
  <p data-lake-id="u9676f194" id="u9676f194"><strong><span data-lake-id="u6acf17b5" id="u6acf17b5">​</span></strong><br></p>
  <p data-lake-id="u8f9c1931" id="u8f9c1931"><strong><span data-lake-id="u69b5c783" id="u69b5c783">​</span></strong><br></p>
  <h1 data-lake-id="nvA36" id="nvA36"><span data-lake-id="u92451d72" id="u92451d72">扩展知识</span></h1>
  <p data-lake-id="uc93886aa" id="uc93886aa"><br></p>
  <h2 data-lake-id="Rxfdg" id="Rxfdg"><span data-lake-id="u33b81951" id="u33b81951">顺序背后的原理</span></h2>
  <p data-lake-id="u97b62bcd" id="u97b62bcd"><br></p>
  <p data-lake-id="u94acf20b" id="u94acf20b"><span data-lake-id="uc17526c1" id="uc17526c1">这个执行顺序，难道要死记硬背吗？那就太low了，其实这个执行顺序，如果大家看过下面这篇之后，就会理所应当的知道了。</span></p>
  <p data-lake-id="ud4f05b6a" id="ud4f05b6a"><span data-lake-id="u4410b807" id="u4410b807">​</span><br></p>
  <p data-lake-id="u6cf97643" id="u6cf97643"><br></p>
  <p data-lake-id="uaa771825" id="uaa771825"><span data-lake-id="u84aaa694" id="u84aaa694">在Bean的初始化过程中，在initializeBean方法中，会调用invokeInitMethods方法：</span></p>
  <p data-lake-id="u1ef79ea9" id="u1ef79ea9"><span data-lake-id="u66d98586" id="u66d98586">​</span><br></p>
  <p data-lake-id="u577357b1" id="u577357b1"><span data-lake-id="u21e06292" id="u21e06292">​</span><br></p>
  <pre lang="java"><code>
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    invokeAwareMethods(beanName, bean);

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}
</code></pre>
  <p data-lake-id="ub91b1dc5" id="ub91b1dc5"><br></p>
  <p data-lake-id="u0ec0034f" id="u0ec0034f"><br></p>
  <p data-lake-id="ue15f5b56" id="ue15f5b56"><span data-lake-id="ubaa3cec2" id="ubaa3cec2">invokeInitMethods方法有两个if分支，第一个分支就是执行afterPropertiesSet方法的，第二个分支就是执行initMethod的，所以，一定是先执行afterPropertiesSet后执行initMethod。</span></p>
  <pre lang="java"><code>
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
        throws Throwable {

    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean &amp;&amp; (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
        if (logger.isTraceEnabled()) {
            logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
        }
        ((InitializingBean) bean).afterPropertiesSet();
    }

    if (mbd != null &amp;&amp; bean.getClass() != NullBean.class) {
        String[] initMethodNames = mbd.getInitMethodNames();
        if (initMethodNames != null) {
            for (String initMethodName : initMethodNames) {
                if (StringUtils.hasLength(initMethodName) &amp;&amp;
                        !(isInitializingBean &amp;&amp; "afterPropertiesSet".equals(initMethodName)) &amp;&amp;
                        !mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {
                    invokeCustomInitMethod(beanName, bean, mbd, initMethodName);
                }
            }
        }
    }
}
</code></pre>
  <p data-lake-id="ufdb40ec8" id="ufdb40ec8"><br></p>
  <p data-lake-id="u97e7ad07" id="u97e7ad07"><span data-lake-id="u34390bd1" id="u34390bd1">那么，@PostConstruct的执行在哪个过程呢？</span></p>
  <p data-lake-id="ucc59a033" id="ucc59a033"><span data-lake-id="u02ddba25" id="u02ddba25">​</span><br></p>
  <p data-lake-id="u1d8bfe94" id="u1d8bfe94"><span data-lake-id="u128e9fd8" id="u128e9fd8">前面的initializeBean方法中，在调用invokeInitMethods之前，会调用applyBeanPostProcessorsBeforeInitialization，这个就是我们知道的调用BeanPostProcessor的前置处理方法。即调用postProcessBeforeInitialization</span></p>
  <p data-lake-id="uc0b63ba8" id="uc0b63ba8"><span data-lake-id="ueb33d573" id="ueb33d573">​</span><br></p>
  <p data-lake-id="uebe540f6" id="uebe540f6"><span data-lake-id="u817af6b9" id="u817af6b9">这里就需要登场一个InitDestroyAnnotationBeanPostProcessor了，看名字很容易知道，他就是在Bean初始化和销毁过程中的一个处理器。那么我们继续看postProcessBeforeInitialization方法：</span></p>
  <p data-lake-id="u377ad90b" id="u377ad90b"><span data-lake-id="ue89c7551" id="ue89c7551">​</span><br></p>
  <pre lang="java"><code>
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
    try {
        metadata.invokeInitMethods(bean, beanName);
    }
    catch (InvocationTargetException ex) {
        throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
    }
    return bean;
}

</code></pre>
  <p data-lake-id="ucf13e2a7" id="ucf13e2a7"><span data-lake-id="u8298fa85" id="u8298fa85">​</span><br></p>
  <p data-lake-id="u9cf7743f" id="u9cf7743f"><span data-lake-id="ud0369175" id="ud0369175">这有一段熟悉的代码了，那就是invokeInitMethods，好像和我们要找的方法有关，看看metadata是咋来的，就看findLifecycleMetadata：</span></p>
  <p data-lake-id="u6798e212" id="u6798e212"><span data-lake-id="u46adaf5e" id="u46adaf5e">​</span><br></p>
  <pre lang="java"><code>
private LifecycleMetadata findLifecycleMetadata(Class&lt;?&gt; beanClass) {
    if (this.lifecycleMetadataCache == null) {
        // Happens after deserialization, during destruction...
        return buildLifecycleMetadata(beanClass);
    }
    // Quick check on the concurrent map first, with minimal locking.
    LifecycleMetadata metadata = this.lifecycleMetadataCache.get(beanClass);
    if (metadata == null) {
        synchronized (this.lifecycleMetadataCache) {
            metadata = this.lifecycleMetadataCache.get(beanClass);
            if (metadata == null) {
                metadata = buildLifecycleMetadata(beanClass);
                this.lifecycleMetadataCache.put(beanClass, metadata);
            }
            return metadata;
        }
    }
    return metadata;
}

</code></pre>
  <p data-lake-id="uafba1368" id="uafba1368"><span data-lake-id="u2e44bebe" id="u2e44bebe">​</span><br></p>
  <p data-lake-id="u3d296051" id="u3d296051"><span data-lake-id="uab61cec9" id="uab61cec9">看到这里面的核心逻辑就是buildLifecycleMetadata，继续看buildLifecycleMetadata方法：</span></p>
  <p data-lake-id="u0f5086a3" id="u0f5086a3"><span data-lake-id="u762a8d7d" id="u762a8d7d">​</span><br></p>
  <pre lang="java"><code>
private LifecycleMetadata buildLifecycleMetadata(final Class&lt;?&gt; beanClass) {
    if (!AnnotationUtils.isCandidateClass(beanClass, this.initAnnotationTypes) &amp;&amp;
            !AnnotationUtils.isCandidateClass(beanClass, this.destroyAnnotationTypes)) {
        return this.emptyLifecycleMetadata;
    }

    List&lt;LifecycleMethod&gt; initMethods = new ArrayList&lt;&gt;();
    List&lt;LifecycleMethod&gt; destroyMethods = new ArrayList&lt;&gt;();
    Class&lt;?&gt; currentClass = beanClass;

    do {
        final List&lt;LifecycleMethod&gt; currInitMethods = new ArrayList&lt;&gt;();
        final List&lt;LifecycleMethod&gt; currDestroyMethods = new ArrayList&lt;&gt;();

        ReflectionUtils.doWithLocalMethods(currentClass, method -&gt; {
            for (Class&lt;? extends Annotation&gt; initAnnotationType : this.initAnnotationTypes) {
                if (initAnnotationType != null &amp;&amp; method.isAnnotationPresent(initAnnotationType)) {
                    currInitMethods.add(new LifecycleMethod(method, beanClass));
                    if (logger.isTraceEnabled()) {
                        logger.trace("Found init method on class [" + beanClass.getName() + "]: " + method);
                    }
                }
            }
            for (Class&lt;? extends Annotation&gt; destroyAnnotationType : this.destroyAnnotationTypes) {
                if (destroyAnnotationType != null &amp;&amp; method.isAnnotationPresent(destroyAnnotationType)) {
                    currDestroyMethods.add(new LifecycleMethod(method, beanClass));
                    if (logger.isTraceEnabled()) {
                        logger.trace("Found destroy method on class [" + beanClass.getName() + "]: " + method);
                    }
                }
            }
        });

        initMethods.addAll(0, currInitMethods);
        destroyMethods.addAll(currDestroyMethods);
        currentClass = currentClass.getSuperclass();
    }
    while (currentClass != null &amp;&amp; currentClass != Object.class);

    return (initMethods.isEmpty() &amp;&amp; destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
            new LifecycleMetadata(beanClass, initMethods, destroyMethods));
}

</code></pre>
  <p data-lake-id="ua16156d8" id="ua16156d8"><span data-lake-id="u050b4508" id="u050b4508"></span></p>
  <p data-lake-id="u26dcafe2" id="u26dcafe2"><span data-lake-id="u07415270" id="u07415270">这里其实就是在寻找这个bean中有没有哪些方法上有initAnnotationType注解，如果有的话，那么就把他们添加到initMethods中，返回给前面的方法执行。</span></p>
  <p data-lake-id="u13110f69" id="u13110f69"><span data-lake-id="u99669b92" id="u99669b92">​</span><br></p>
  <p data-lake-id="u3f480bec" id="u3f480bec"><span data-lake-id="uc50b2f53" id="uc50b2f53">这里的initAnnotationTypes是一个集合：</span></p>
  <p data-lake-id="u3ce636f1" id="u3ce636f1"><span data-lake-id="uca64c28b" id="uca64c28b">​</span><br></p>
  <pre lang="java"><code>
private final Set&lt;Class&lt;? extends Annotation&gt;&gt; initAnnotationTypes = new LinkedHashSet&lt;&gt;(2);


public void addInitAnnotationType(@Nullable Class&lt;? extends Annotation&gt; initAnnotationType) {
    if (initAnnotationType != null) {
        this.initAnnotationTypes.add(initAnnotationType);
    }
}
</code></pre>
  <p data-lake-id="u76188dcb" id="u76188dcb"><br></p>
  <p data-lake-id="u0dfe20d8" id="u0dfe20d8"><span data-lake-id="ua23ad469" id="ua23ad469">看看他是从哪设置进来的就行了，直接找他在哪里被调用了，会发现在CommonAnnotationBeanPostProcessor中有调用：</span></p>
  <p data-lake-id="u502a0df4" id="u502a0df4"><span data-lake-id="u7f198f4c" id="u7f198f4c">​</span><br></p>
  <pre lang="java"><code>
public CommonAnnotationBeanPostProcessor() {
    setOrder(Ordered.LOWEST_PRECEDENCE - 3);

    // Jakarta EE 9 set of annotations in jakarta.annotation package
    addInitAnnotationType(loadAnnotationType("jakarta.annotation.PostConstruct"));
    addDestroyAnnotationType(loadAnnotationType("jakarta.annotation.PreDestroy"));

    // Tolerate legacy JSR-250 annotations in javax.annotation package
    addInitAnnotationType(loadAnnotationType("javax.annotation.PostConstruct"));
    addDestroyAnnotationType(loadAnnotationType("javax.annotation.PreDestroy"));

    // java.naming module present on JDK 9+?
    if (jndiPresent) {
        this.jndiFactory = new SimpleJndiBeanFactory();
    }
}

</code></pre>
  <p data-lake-id="uab2fc464" id="uab2fc464"><span data-lake-id="u4a2527ba" id="u4a2527ba"></span></p>
  <p data-lake-id="u9ce89ac4" id="u9ce89ac4"><span data-lake-id="u3d8091da" id="u3d8091da">因为我用的是Spring Boot 3.0，SpringBoot 2.0的代码长这样：</span></p>
  <pre lang="java"><code>
public CommonAnnotationBeanPostProcessor() {
    setOrder(Ordered.LOWEST_PRECEDENCE - 3);
    setInitAnnotationType(PostConstruct.class);
    setDestroyAnnotationType(PreDestroy.class);
    ignoreResourceType("javax.xml.ws.WebServiceContext");
}
</code></pre>
  <p data-lake-id="ub83f4043" id="ub83f4043"><span data-lake-id="u8bc5dba1" id="u8bc5dba1">​</span><br></p>
  <p data-lake-id="u26f93dad" id="u26f93dad"><span data-lake-id="ub4194a31" id="ub4194a31">看到了吧，在这里设置了PostConstruct这个注解作为InitAnnotationType。</span></p>
  <p data-lake-id="u743a36aa" id="u743a36aa"><span data-lake-id="u10c4dd3c" id="u10c4dd3c">​</span><br></p>
  <p data-lake-id="u18cac968" id="u18cac968"><span data-lake-id="ufc2fc993" id="ufc2fc993">总结一下，在CommonAnnotationBeanPostProcessor初始化的时候，会把PostConstruct设置到initAnnotationTypes中，然后在InitDestroyAnnotationBeanPostProcessor的postProcessBeforeInitialization方法执行过程中，会检查这个Bean中有哪些initAnnotationType，把加了initAnnotationType注解的方法当做初始化方法进行调用。</span></p>
  <p data-lake-id="u6c855a6f" id="u6c855a6f"><span data-lake-id="ua2848e38" id="ua2848e38">​</span><br></p>
  <p data-lake-id="u76b9af22" id="u76b9af22"><span data-lake-id="u3713cb1a" id="u3713cb1a">所以，@PostConstruct 这个注解就在这个阶段被调用了。</span></p>
  <p data-lake-id="u07e99324" id="u07e99324"><span data-lake-id="u47c2fe89" id="u47c2fe89">​</span><br></p>
  <p data-lake-id="ufd7f26f7" id="ufd7f26f7"><span data-lake-id="u5cb9a7d2" id="u5cb9a7d2">​</span><br></p>
 </body>
</html>